use std::net::SocketAddr;
use confsync::pb::{ReportRequest, ReportResponse,manage_server::Manage,manage_server::ManageServer};
use actix_web::{web, App, HttpServer};
use confsync::handlers::{agent, notify, register_config, test_db};
use confsync::db::init_server_db;
use rusqlite::{params, Connection};
use walkdir::WalkDir;
use confsync::pb::{agent_client::AgentClient,SyncRequest,File as ConfigFile};
#[derive(Debug, Default)]
pub struct ManageService;

#[tonic::async_trait]
impl Manage for ManageService {
    async fn report(&self, request: tonic::Request<ReportRequest>) -> Result<tonic::Response<ReportResponse>, tonic::Status> {
        let addr = request.remote_addr().unwrap();
        sync(&addr).await;
        let response = ReportResponse {
            success: true,
        };
        Ok(tonic::Response::new(response))
    }
}

async fn sync(remote_addr: &SocketAddr) {
    let path = get_config_path(remote_addr).unwrap();
    let files = WalkDir::new(&path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.path().is_file())
        .filter(|e| e.path().to_str().unwrap().contains(".git") == false)
        .map(|e| {
            let path = e.path().to_str().unwrap().to_string();
            let content = std::fs::read(e.path()).unwrap();
            ConfigFile {
                path,
                content,
            }
        })
        .collect::<Vec<ConfigFile>>();
    let mut client = AgentClient::connect(format!("http://{}", remote_addr))
        .await
        .unwrap();
    let request = tonic::Request::new(SyncRequest { path, files });
    let response = client.sync(request).await.unwrap().into_inner();

    let report_response = ReportResponse {
        success: response.success,
    };
    println!("Response: {:?}", report_response);
}

fn get_config_path(remote_addr: &SocketAddr) -> rusqlite::Result<String> {
    let conn = Connection::open("server_database.db")?;
    let mut stmt = conn.prepare(
        "
        SELECT configs.path
        FROM agents
        INNER JOIN configs ON agents.config = configs.name
        WHERE agents.addr = ?
    ",
    )?;
    let path = stmt
        .query_map(params![remote_addr.to_string()], |row| {
            Ok(row.get::<_, String>(0)?)
        })?
        .next()
        .unwrap()?;
    Ok(path)
}

#[tokio::main]
async fn main(){
    let grpc_task=async move{
        bootstrap_grpc_server().await;
    };
    let http_task=async move{
        _=bootstrap_http_server().await;
    };
    tokio::join!(grpc_task,http_task);
}

async fn bootstrap_grpc_server(){
    let addr = "[::1]:50051".parse().unwrap();
    let manage = ManageService::default();

    println!("ManageServer listening on {}", addr);

    tonic::transport::Server::builder()
        .add_service(ManageServer::new(manage))
        .serve(addr)
        .await
        .unwrap();
}

async fn bootstrap_http_server()->std::io::Result<()>{
    init_server_db().unwrap();
	HttpServer::new(|| {
		App::new()
            .route("register", web::post().to(register_config))
            .route("notify",web::post().to(notify)) 
            .route("test", web::get().to(test_db))
            .route("agent", web::post().to(agent))
	})
	.bind("127.0.0.1:8000")?
	.run()
	.await
}